﻿//  -------------------- iSharePoint -------------------- //
// 
//  Company:	IT Complex, LLC
//  Project:	Smart.Documents
//  File name:	LookupFieldWithPickerQuery.cs
//  Developer:	Solomatov Igor
//  Created:	16.04.2012
// 
//  -------------------- iSharePoint -------------------- //

using System;
using System.Data;
using System.Globalization;
using System.Text;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.WebControls;
using Smart.Common;

// ReSharper disable CheckNamespace

namespace Smart.Documents.Fields.LookupFieldWithPicker
// ReSharper restore CheckNamespace
{
	/// <summary>
	/// This class is really dirty, sorry. It overrides, extends, copies and hacks code from the base class.
	/// </summary>
	public class LookupFieldWithPickerQuery : SimpleQueryControl, ICallbackEventHandler
	{
		// ReSharper disable InconsistentNaming
		private LookupFieldWithPickerPropertyBag propertyBag;
		private SPWeb web;
		private SPList list;
		private DataTable dataTable;

		private string searchField;
		private string searchOperator;

		//should be a select not dropdown, because of EventValidation issue of AJAX like functionality with WebControls
		protected HtmlSelect drpdSearchOperators;
		// ReSharper restore InconsistentNaming

		protected override void OnLoad(EventArgs e)
		{
			base.OnLoad(e);

			propertyBag = new LookupFieldWithPickerPropertyBag(PickerDialog.CustomProperty);

			web = SPContext.Current.Site.OpenWeb(propertyBag.WebId);

			list = web.Lists[propertyBag.ListId];
		}

		protected override void OnUnload(EventArgs e)
		{
			if (web != null)
			{
				// ReSharper disable EmptyGeneralCatchClause
				try
				{
					web.Dispose();
					web = null;
				}
				catch
				{
					// already disposed?
				}
				// ReSharper restore EmptyGeneralCatchClause
			}

			base.OnUnload(e);
		}

		protected override void CreateChildControls()
		{
			dataTable = new DataTable();

			var htmlTable = new Table { Width = Unit.Percentage(100.0) };
			htmlTable.Attributes.Add("cellspacing", "0");
			htmlTable.Attributes.Add("cellpadding", "0");
			var row = new TableRow { Width = Unit.Percentage(100.0) };
			var cell = new TableCell { CssClass = "ms-descriptiontext" };
			cell.Attributes.Add("style", "white-space:nowrap");
			var str = SPHttpUtility.HtmlEncode(SPResource.GetString("PickerDialogDefaultSearchLabel", new object[0]));
			str = string.Format(CultureInfo.InvariantCulture, "<b>{0}</b>&nbsp;", str);
			var label = new Label { Text = str };
			cell.Controls.Add(label);
			ColumnList = new DropDownList { ID = "columnList", CssClass = "ms-pickerdropdown" };
			cell.Controls.Add(ColumnList);
			row.Cells.Add(cell);

			//Punches-in the search operator dropdown
			cell = new TableCell();
			cell.Style.Add("padding-right", "20px");
			drpdSearchOperators = new HtmlSelect { ID = "queryOperators" };
			drpdSearchOperators.Attributes.Add("class", "ms-pickerdropdown");

			cell.Controls.Add(drpdSearchOperators);
			row.Cells.Add(cell);

			cell = new TableCell { Width = Unit.Percentage(100.0) };
			QueryTextBox = new InputFormTextBox
							{
								ID = "queryTextBox",
								CssClass = "ms-pickersearchbox",
								AccessKey = SPResource.GetString("PickerDialogSearchAccessKey", new object[0]),
								Width = Unit.Percentage(100.0),
								MaxLength = 0x3e8,
								Text = QueryText
							};
			cell.Controls.Add(QueryTextBox);
			row.Cells.Add(cell);
			label.AssociatedControlID = "queryTextBox";

			cell = new TableCell();
			QueryButton = new ImageButton
							{
								ID = "queryButton",
								OnClientClick = "executeQuery();return false;",
								ToolTip = SPResource.GetString("PickerDialogSearchToolTip", new object[0]),
								AlternateText = SPResource.GetString("PickerDialogSearchToolTip", new object[0]),
								ImageUrl =
									web.RegionalSettings.IsRightToLeft ? "/_layouts/images/gortl.gif" : "/_layouts/images/gosearch.gif"
							};

			var control = new HtmlGenericControl("div");
			control.Attributes.Add("class", "ms-searchimage");
			control.Controls.Add(QueryButton);
			cell.Controls.Add(control);
			row.Cells.Add(cell);
			htmlTable.Rows.Add(row);
			Controls.Add(htmlTable);

			//fills the search fields initially  
			foreach (SPField field in list.Fields)
			{
				var searchFields = propertyBag.SearchFields;

				// add table columns
				if (!searchFields.Contains(field.InternalName) && field.Id != propertyBag.FieldId)
					continue;

				if (!Page.IsPostBack)
				{
					mColumnList.Items.Add(new ListItem(field.Title, field.Id.ToString()));
				}

				var col = dataTable.Columns.Add();
				col.ColumnName = field.Id.ToString();
				col.Caption = field.Title;
				col.ExtendedProperties.Add("InternalName", field.InternalName);
			}

			if (!dataTable.Columns.Contains(SPBuiltInFieldId.ID.ToString()))
			{
				var idField = list.Fields[SPBuiltInFieldId.ID];

				var col = dataTable.Columns.Add();
				col.ColumnName = idField.Id.ToString();
				col.Caption = idField.Title;
				col.ExtendedProperties.Add("InternalName", idField.InternalName);
			}

			if (mColumnList.Items.Count == 0)
			{
				var field = list.Fields[propertyBag.FieldId];
				mColumnList.Items.Add(new ListItem(field.Title, field.Id.ToString()));
			}

			//fills the search operators initally
			FillSearchOperators(ColumnList.SelectedValue);
		}

		protected override void OnPreRender(EventArgs e)
		{
			//generate callback script for search field changes
			var cs = Page.ClientScript;

			var context = GenerateContextScript();
			var cbr = cs.GetCallbackEventReference(this, "searchField", "SearchFieldChangedResult", context, false);
			var callbackScript = "function SearchFieldChanged() {"
								 + "var searchField= 'searchFieldChangedTo:' + document.getElementById('" + SPHttpUtility.EcmaScriptStringLiteralEncode(ColumnList.ClientID) + "').value;"
								 + cbr + "; }";

			cs.RegisterClientScriptBlock(GetType(), "SearchFieldChanged",
										 callbackScript, true);

			ColumnList.Attributes.Add("onchange", "SearchFieldChanged();");

			//HACK: fragment from the base class with query operators hack
			var str = Page.ClientScript.GetCallbackEventReference(this, "search", "PickerDialogHandleQueryResult", "ctx", "PickerDialogHandleQueryError", true);

			Page.ClientScript.RegisterClientScriptBlock(GetType(), "__SEARCH__",
														"<script>\r\n                function executeQuery()\r\n                {\r\n   var operators=document.getElementById('" +
														SPHttpUtility.EcmaScriptStringLiteralEncode(drpdSearchOperators.ClientID) + "');                 var query=document.getElementById('" +
														SPHttpUtility.EcmaScriptStringLiteralEncode(QueryTextBox.ClientID) + "');\r\n                    var list=document.getElementById('" +
														SPHttpUtility.EcmaScriptStringLiteralEncode(ColumnList.ClientID) +
														"');\r\n                    var search='';\r\n                    var multiParts = new Array();\r\n       multiParts.push(operators.value);\r\n             if(list!=null)\r\n                        multiParts.push(list.value);\r\n                    else\r\n                        multiParts.push('');\r\n                    multiParts.push(query.value);\r\n\r\n                    search = ConvertMultiColumnValueToString(multiParts);\r\n                    PickerDialogSetClearState();\r\n                    \r\n                    var ctx = new PickerDialogCallbacContext();\r\n                    ctx.resultTableId = 'resultTable';\r\n                    ctx.queryTextBoxElementId = '" +
														SPHttpUtility.EcmaScriptStringLiteralEncode(QueryTextBox.ClientID) + "';\r\n                    ctx.errorElementId = '" +
														SPHttpUtility.EcmaScriptStringLiteralEncode(PickerDialog.ErrorLabel.ClientID) +
														"';\r\n                    ctx.htmlMessageElementId = '" +
														SPHttpUtility.EcmaScriptStringLiteralEncode(PickerDialog.HtmlMessageLabel.ClientID) +
														"';\r\n                    ctx.queryButtonElementId = '" + SPHttpUtility.EcmaScriptStringLiteralEncode(QueryButton.ClientID) +
														"';\r\n                    PickerDialogShowWait(ctx);\r\n                    " + str +
														";\r\n                }\r\n                </script>");
			QueryTextBox.Text = QueryText;
			QueryTextBox.Attributes.Add("onKeyDown",
										"var e=event; if(!e) e=window.event; if(!browseris.safari && e.keyCode==13) { document.getElementById('" +
										SPHttpUtility.EcmaScriptStringLiteralEncode(QueryButton.ClientID) + "').click(); return false; }");

			// To disable auto-populate, add "(QueryTextBox.Text.Length > 0) &&"
			if (!Page.IsPostBack)
			{
				var group = string.Empty;

				if (ColumnList.SelectedItem != null)
				{
					group = ColumnList.SelectedItem.Value;
				}

				ExecuteQuery(group, QueryText);
			}

			Page.ClientScript.RegisterStartupScript(GetType(), "SetFocus",
													"<script>\r\n                    var objQueryTextBox = document.getElementById('" +
													SPHttpUtility.EcmaScriptStringLiteralEncode(QueryTextBox.ClientID) +
													"'); \r\n                    if (objQueryTextBox != null)\r\n                    {\r\n                        try\r\n                        {\r\n                            objQueryTextBox.focus();\r\n                        }\r\n                        catch(e)\r\n                        {\r\n                        }\r\n                    }\r\n                  </script>");
		}

		private string GenerateContextScript()
		{
			var context = new StringBuilder();

			context.Append("function SearchFieldChangedResult(searchOperators, context)");
			context.Append("{");
			context.Append("var drpdSearchOperators = document.getElementById('" + SPHttpUtility.EcmaScriptStringLiteralEncode(drpdSearchOperators.ClientID) + "');");
			context.Append("drpdSearchOperators.length=0;");
			context.Append("var operators = searchOperators.split(';');");
			context.Append("for(op=0;op<operators.length;op++)");
			context.Append("{");
			context.Append("var operator = operators[op].split(',');");
			context.Append("var option = document.createElement('option');");
			context.Append("option.text = operator[0];");
			context.Append("option.value = operator[1];");
			context.Append("drpdSearchOperators.add(option);");
			context.Append("}");
			context.Append("}");

			return context.ToString();
		}

		private void FillSearchOperators(string pSearchField)
		{
			drpdSearchOperators.Items.Clear();

			var queryField = list.Fields[new Guid(pSearchField)];

			drpdSearchOperators.Items.Add(new ListItem(LookupFieldWithPickerHelper.GetResourceString("lookupWithPickerOperatorEquals"), "Eq"));
			drpdSearchOperators.Items.Add(new ListItem(LookupFieldWithPickerHelper.GetResourceString("lookupWithPickerOperatorNotEqual"), "Neq"));

			if (queryField.Type == SPFieldType.Counter || queryField.Type == SPFieldType.Integer
				|| queryField.Type == SPFieldType.Number || queryField.Type == SPFieldType.Currency
				|| queryField.Type == SPFieldType.DateTime)
			{
				drpdSearchOperators.Items.Add(new ListItem(LookupFieldWithPickerHelper.GetResourceString("lookupWithPickerOperatorLessThan"), "Lt"));
				drpdSearchOperators.Items.Add(new ListItem(LookupFieldWithPickerHelper.GetResourceString("lookupWithPickerOperatorLessThanOrEqualTo"), "Leq"));
				drpdSearchOperators.Items.Add(new ListItem(LookupFieldWithPickerHelper.GetResourceString("lookupWithPickerOperatorGreaterThan"), "Gt"));
				drpdSearchOperators.Items.Add(new ListItem(LookupFieldWithPickerHelper.GetResourceString("lookupWithPickerOperatorGreaterThanOrEqualTo"), "Geq"));
			}
			else
			{
				if (queryField.Type != SPFieldType.Boolean && queryField.Type != SPFieldType.DateTime)
				{
					drpdSearchOperators.Items.Insert(0, new ListItem(LookupFieldWithPickerHelper.GetResourceString("lookupWithPickerOperatorContains"), "Contains"));
				}

				drpdSearchOperators.Items.Insert(1, new ListItem(LookupFieldWithPickerHelper.GetResourceString("lookupWithPickerOperatorBeginsWith"), "BeginsWith"));
			}
		}

		public new void RaiseCallbackEvent(string eventArgument)
		{
			//Wraps the base method to cut the hacked-in search operator

			if (eventArgument.StartsWith("searchFieldChangedTo:"))
			{
				searchField = eventArgument.Replace("searchFieldChangedTo:", "");
			}
			else
			{
				var multiVal = new SPFieldMultiColumnValue(eventArgument);

				if (multiVal.Count == 3)
				{
					searchOperator = multiVal[0];
					eventArgument = eventArgument.Replace(";#" + searchOperator, "");

					base.RaiseCallbackEvent(eventArgument);
				}
				else
				{
					base.RaiseCallbackEvent(eventArgument);
				}
			}
		}

		public new string GetCallbackResult()
		{
			if (String.IsNullOrEmpty(searchField))
				return base.GetCallbackResult();

			FillSearchOperators(searchField);

			var operators = "";

			foreach (ListItem item in drpdSearchOperators.Items)
			{
				if (operators.Length >= 1)
					operators += ";";

				operators += item.Text + "," + item.Value;
			}

			return operators;
		}

		public override PickerEntity GetEntity(DataRow row)
		{
			var entity = new PickerEntity
							{
								DisplayText = row[propertyBag.FieldId.ToString()].ToString(),
								Key = row[SPBuiltInFieldId.ID.ToString()].ToString()
							};

			entity.Description = entity.DisplayText;
			entity.IsResolved = true;

			foreach (DataColumn col in row.Table.Columns)
			{
				foreach (var internalName in propertyBag.SearchFields)
				{
					if (internalName != col.ExtendedProperties["InternalName"].ToString())
						continue;

					entity.EntityData.Add(col.ColumnName, row[col]);

					break;
				}
			}

			return entity;
		}

		protected override int IssueQuery(string search, string groupName, int pageIndex, int pageSize)
		{
			var table = GetListTable(search, groupName);

			PickerDialog.Results = table;
			PickerDialog.ResultControl.PageSize = table.Rows.Count;

			return table.Rows.Count;
		}

		private DataTable GetListTable(string search, string groupName)
		{
			var lSearchField = list.Fields[new Guid(groupName)];
			var lSearchQuery = propertyBag.SearchQuery;

			var maxSearchResults = (uint)propertyBag.MaxSearchResults;

			if (maxSearchResults > 0)
				maxSearchResults++;

			try
			{
				SPListItemCollection items;

				if (!string.IsNullOrEmpty(search))
				{
					if (lSearchField.Type == SPFieldType.DateTime)
						search = SPUtility.CreateISO8601DateTimeFromSystemDateTime(DateTime.Parse(search));

					var valueType = lSearchField.TypeAsString;

					if (lSearchField.Type == SPFieldType.Calculated)
						valueType = "Text";

					var query =
						string.IsNullOrEmpty(lSearchQuery)
							? string.Format(
								"<Where><{0}><FieldRef ID=\"{1}\"/><Value Type=\"{2}\">{3}</Value></{0}></Where>",
								searchOperator ?? "Eq",
								lSearchField.Id,
								valueType,
								search)
							: string.Format(
								"<Where><And><{0}><FieldRef ID=\"{1}\"/><Value Type=\"{2}\">{3}</Value></{0}>{4}</And></Where>",
								searchOperator ?? "Eq",
								lSearchField.Id,
								valueType,
								search,
								lSearchQuery);

					items = SPH.GetFilteredItems(list, query, maxSearchResults, null);
				}
				else if (!string.IsNullOrEmpty(lSearchQuery))
				{
					var query = string.Concat("<Where>", lSearchQuery, "</Where>");

					items = SPH.GetFilteredItems(list, query, maxSearchResults, null);
				}
				else
				{
					items = SPH.GetFilteredItems(list, null, maxSearchResults, null);
				}

				if (propertyBag.MaxSearchResults > 0 && items.Count > propertyBag.MaxSearchResults)
				{
					PickerDialog.ErrorMessage =
						string.Concat(
							LookupFieldWithPickerHelper.GetResourceString("lookupWithPickerSearchResultExceededMessage"),
							" (", propertyBag.MaxSearchResults, ")");
				}

				if (items != null)
				{
					foreach (SPListItem item in items)
					{
						var row = dataTable.NewRow();

						foreach (DataColumn col in dataTable.Columns)
						{
							try
							{
								var field = item.Fields[new Guid(col.ColumnName)];

								row[col] = field.GetFieldValueAsText(item[field.Id]);
							}
							catch (Exception x)
							{
								H.LX("LookupFieldWithPickerQuery.GetListTable.I", x);
							}
						}

						dataTable.Rows.Add(row);

						if (propertyBag.MaxSearchResults > 0 && dataTable.Rows.Count == propertyBag.MaxSearchResults)
							break;
					}
				}
			}
			catch (Exception x)
			{
				H.LX("LookupFieldWithPickerQuery.GetListTable", x);
			}

			return dataTable;
		}
	}
}